package cs.android.viewbase;

import static cs.android.lang.AndroidLang.event;
import static cs.java.lang.Lang.fire;
import static cs.java.lang.Lang.is;
import static cs.java.lang.Lang.no;
import static cs.java.lang.Lang.set;
import static cs.java.lang.Lang.unexpected;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import cs.android.ActivityView;
import cs.java.event.Event;
import cs.java.event.EventConsumer;
import cs.java.event.EventConsumerImpl;
import cs.java.event.Job;
import cs.java.event.Task;
import cs.java.lang.Value;

public abstract class ActivityViewBase extends ViewPresenterImpl implements ActivityView {

	protected static LayoutId layout(int id) {
		return new LayoutId(id);
	}

	protected Activity activity;

	private final Event<Void> eventPause = event();
	private final Event<Bundle> eventCreate = event();
	private final Event<Value<Boolean>> onBack = event();
	private final Event<Void> eventStart = event();
	private final Event<Void> eventStop = event();
	private final Event<Bundle> eventSaveInstance = event();
	private final Event<Void> eventDestroy = event();
	private final Event<Void> eventResume = event();
	private final Event<ActivityResult> eventActivityResult = event();
	private boolean paused = true;
	private boolean started = false;
	private boolean destroyed;
	private boolean created;
	private Bundle state;
	protected ActivityView parent;
	private int viewId;
	private final EventConsumer parentEventRegistrations = new EventConsumerImpl();
	private LayoutId layoutId;

	public ActivityViewBase(ActivityView parent) {
		this.parent = parent;
		listenIsActivity(parent);
	}

	public ActivityViewBase(ActivityView parent, int viewId) {
		this.parent = parent;
		this.viewId = viewId;
		if (is(parent)) listenIsActivity(parent);
	}

	public ActivityViewBase(ActivityView parent, LayoutId id) {
		this.parent = parent;
		layoutId = id;
		if (is(parent)) listenIsActivity(parent);
	}

	public ActivityViewBase(int layoutId) {
		this.layoutId = layout(layoutId);
	}

	public ActivityViewBase(LayoutId layoutId) {
		this.layoutId = layoutId;
	}

	protected Bundle getIntentExtras() {
		return getActivity().getIntent().getExtras();
	}

	protected void goBack() {
		getActivity().onBackPressed();
	}

	protected boolean isFirstTime() {
		return no(state);
	}

	protected boolean isMainActionRun() {
		return Intent.ACTION_MAIN.equals(getActivity().getIntent().getAction());
	}

	protected void onActivityResult(int requestCode, int resultCode, Intent data) {
		fire(eventActivityResult, new ActivityResult(requestCode, resultCode, data));
	}

	protected void onBackPressed(Value<Boolean> goBack) {
		fire(onBack, goBack);
	}

	/**
	 * initialize view
	 */
	protected void onCreate() {}

	protected void onCreate(Bundle state) {
		if (is(parent)) setActivity(parent.getActivity());
		this.state = state;
		fire(eventCreate, state);
		onCreate();
		if (no(state)) onCreateFirstTime();
		else onCreateRestore(state);
		created = true;
	}

	/**
	 * initialize view
	 */
	protected void onCreateFirstTime() {}

	/**
	 * initialize view
	 */
	protected void onCreateRestore(Bundle state) {}

	protected void onDestroy() {
		destroyed = true;
		state = null;
		setActivity(null);
		setView(null);
		fire(eventDestroy);
	}

	protected void onPause() {
		paused = true;
		fire(eventPause);
		cancelRegistrations();
	}

	/**
	 * update view
	 */
	protected void onResume() {
		paused = false;
		fire(eventResume);
		if (isFirstTime()) onResumeFirstTime();
		else onResumeRestore();
	}

	protected void onResumeFirstTime() {}

	protected void onResumeRestore() {}

	protected void onSaveInstanceState(Bundle state) {
		fire(eventSaveInstance, state);
	}

	protected void overridePendingTransition(int in, int out) {
		getActivity().overridePendingTransition(in, out);
	}

	protected void setContentView(int layoutResId) {
		getActivity().setContentView(layoutResId);
	}

	protected void startActivity(Class<? extends Activity> activityClass) {
		startActivity(new Intent(getActivity(), activityClass));
	}

	protected void startActivity(Intent intent) {
		getActivity().startActivity(intent);
	}

	protected void startActivityForResult(Class<? extends Activity> activityClass, int requestCode) {
		startActivityForResult(new Intent(getActivity(), activityClass), requestCode);
	}

	protected void startActivityForResult(Intent intent, int requestCode) {
		getActivity().startActivityForResult(intent, requestCode);
	}

	protected void switchActivity(Class<? extends Activity> activityClass) {
		switchActivity(new Intent(getActivity(), activityClass));
	}

	protected void switchActivity(Class<? extends Activity> activityClass, int resultCode) {
		getActivity().setResult(resultCode);
		switchActivity(new Intent(getActivity(), activityClass));
	}

	protected void switchActivity(Intent intent) {
		startActivity(intent);
		getActivity().finish();
	}

	private void listenIsActivity(final ActivityView parent) {
		new Task(parentEventRegistrations, parent.getOnCreate(), parent.getOnStart(),
				parent.getOnResume(), parent.getOnPause(), parent.getOnStop(), parent.getOnSaveInstance(),
				parent.getOnDestroy(), parent.getOnBack()) {
			@SuppressWarnings("unchecked") public void run() {
				if (event == parent.getOnCreate()) onCreate((Bundle) argument);
				if (event == parent.getOnStart()) onStart();
				if (event == parent.getOnResume()) onResume();
				if (event == parent.getOnPause()) onPause();
				if (event == parent.getOnStop()) onStop();
				if (event == parent.getOnSaveInstance()) onSaveInstanceState((Bundle) argument);
				if (event == parent.getOnDestroy()) onDestroy();
				if (event == parent.getOnBack()) onBackPressed((Value<Boolean>) argument);
			}
		};
		new Job<ActivityResult>(parentEventRegistrations, parent.getOnActivityResult()) {
			public void run() {
				onActivityResult(argument.requestCode, argument.resultCode, argument.data);
			}
		};
	}

	/**
	 * initialize model objects
	 */
	private final void onStart() {
		started = true;
		fire(eventStart);
	}

	private final void onStop() {
		started = false;
		fire(eventStop);
	}

	void setActivity(Activity activity) {
		this.activity = activity;
		setContext(activity);
	}

	@Override public View asView() {
		if (is(getView())) return getView();
		else if (set(viewId)) {
			setView(parent.asView().findViewById(viewId));
			if (no(getView())) throw unexpected("Expected", this, "in parent", parent);
		} else if (set(layoutId)) setView(inflateLayout(layoutId.value));
		else setView(parent.asView());
		return getView();
	}

	public void deinitialize(Bundle state) {
		onPause();
		onSaveInstanceState(state);
		onStop();
		onDestroy();
		freeFromParent();
	}

	public void freeFromParent() {
		parentEventRegistrations.cancelRegistrations();
	}

	public Activity getActivity() {
		return activity;
	}

	@Override public Event<ActivityResult> getOnActivityResult() {
		return eventActivityResult;
	}

	public Event<Value<Boolean>> getOnBack() {
		return onBack;
	}

	public Event<Bundle> getOnCreate() {
		return eventCreate;
	}

	public Event<Void> getOnDestroy() {
		return eventDestroy;
	}

	public Event<Void> getOnPause() {
		return eventPause;
	}

	public Event<Void> getOnResume() {
		return eventResume;
	}

	public Event<Bundle> getOnSaveInstance() {
		return eventSaveInstance;
	}

	public Event<Void> getOnStart() {
		return eventStart;
	}

	public Event<Void> getOnStop() {
		return eventStop;
	}

	public Bundle getState() {
		return state;
	}

	public int getViewId() {
		return viewId;
	}

	public void initialize(Bundle state) {
		onCreate(state);
		onStart();
		onResume();
	}

	public boolean isCreated() {
		return created;
	}

	public boolean isDestroyed() {
		return destroyed;
	}

	public boolean isPaused() {
		return paused;
	}

	public boolean isResumed() {
		return !paused;
	}

	public boolean isStarted() {
		return started;
	}

	public boolean isStoped() {
		return !started;
	}

	protected static class LayoutId {
		protected final int value;

		public LayoutId(int layoutId) {
			value = layoutId;
		}
	}

	public class ActivityResult {
		public final int requestCode;
		public final int resultCode;
		public final Intent data;

		public ActivityResult(int requestCode, int resultCode, Intent data) {
			this.requestCode = requestCode;
			this.resultCode = resultCode;
			this.data = data;
		}
	}
}
